package com.smsc.headend.task.engine.service.push.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.constant.DeviceModel;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.protocol.cosem.util.ByteNumberUtils;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.protocol.cosem.util.CosemXmlUtils;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.push.EventService;
import com.smsc.headend.task.engine.service.push.ProtocolDataProcess;
import com.smsc.headend.task.engine.utils.CustomMatchUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.regex.Pattern;

/**
 * COSEM规约实现
 *
 * @author liangli
 * @date 2020/10/21
 */
@Slf4j
public class ProtocolDataProcessCosemImpl implements ProtocolDataProcess {
    @Autowired
    EventService eventService;
    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    CosemXmlService cosemXmlService;

    private static final Pattern OBIS_HEX_PTN = Pattern.compile("[0-9A-Fa-f]{12}");

    Set<Long> bpdbPushSpecialDevices = Collections.unmodifiableSet(new HashSet<Long>() {{
        add(Convert.toLong(DeviceModel.Ameter100_BPDB));
        add(Convert.toLong(DeviceModel.Ameter300_BPDB));
        add(Convert.toLong(DeviceModel.HXE310));
        add(Convert.toLong(DeviceModel.HXE330));
        add(Convert.toLong(DeviceModel.HXF300));
        add(Convert.toLong(DeviceModel.HXE130));
        add(Convert.toLong(DeviceModel.DDSY23S));
        add(Convert.toLong(DeviceModel.DTSY23S));
    }});

    @Override
    public int receiveNotificationData(Long deviceType, Long deviceId, Long modelId, String data) {
        CosemData cosemData = CosemXmlUtils.translateCosemXml(data);
        log.info("Device Event: cosemData={}", JSONUtil.toJsonStr(cosemData));
        Long dataItemId = null;
        CosemData notifyData = cosemData;
        // 根据表型来判断进行特殊处理
        if (bpdbPushSpecialDevices.contains(modelId)) {
            assert cosemData != null;
            if ("GatewayRequest".equalsIgnoreCase(cosemData.getType()) || "GatewayResponse".equalsIgnoreCase(cosemData.getType())) {
                if (cosemData.getChildAt(2) != null && cosemData.getChildAt(2).getType().equalsIgnoreCase("DataNotification")) {
                    // 掉电上电暂时进行特殊处理
                    CosemData cosemEventCode = cosemData.getChildAt(2) == null ? null : cosemData.getChildAt(2).getChildAt(2) == null ?
                            null : cosemData.getChildAt(2).getChildAt(2).getChildAt(0) == null ? null : cosemData.getChildAt(2).getChildAt(2).getChildAt(0).getChildAt(0) ==null?
                            null : cosemData.getChildAt(2).getChildAt(2).getChildAt(0).getChildAt(0).getChildAt(3) == null ? null : cosemData.getChildAt(2).getChildAt(2).getChildAt(0).getChildAt(0).getChildAt(3);
                    if (cosemEventCode == null) {
                        log.info("the pushUp pushDown packet is failed to match the sample packet");
                        return 1;
                    }

                    Integer eventCode = Convert.toInt(ByteNumberUtils.byte4ToInt(ByteUtils.hexStringToByte(cosemEventCode.getValue())));
                    eventService.processBPDBNotification(modelId, deviceType, deviceId, -1L, new Date(), eventCode, null, null);
                } else if (cosemData.getChildAt(2) != null && cosemData.getChildAt(2).getType().equalsIgnoreCase("EventNotificationRequest")) {
                    String obis = CustomMatchUtil.getInstanceId(data);
                    if (obis == null) {
                        log.info("the push packet is failed to match the sample packet");
                        return 1;
                    }
                    dataItemId = assetManagementService.getDateItemIdByObisAndModelId(modelId, obis);
                    if (dataItemId == null) {
                        log.info("failed to find the dataItem by the obis {}", obis);
                        return 1;
                    }
                    notifyData = CosemXmlUtils.translateCosemXml(CustomMatchUtil.getAttributeResult(data));
                    eventService.analyseBPDBCOSEMEvent(notifyData, modelId, deviceType, deviceId, dataItemId);
                    return 1;
                } else {
                    log.info("no implementation currently");
                    return 1;
                }
            } else if ((modelId == DeviceModel.Ameter100_BPDB || modelId == DeviceModel.Ameter300_BPDB) && "EventNotificationRequest".equalsIgnoreCase(cosemData.getType())) {
                String obis = CustomMatchUtil.getInstanceId(data);
                if (obis == null) {
                    log.info("the push packet is failed to match the sample packet");
                    return 1;
                }
                dataItemId = assetManagementService.getDateItemIdByObisAndModelId(modelId, obis);
                if (dataItemId == null) {
                    log.info("failed to find the dataItem by the obis {}", obis);
                    return 1;
                }
                notifyData = CosemXmlUtils.translateCosemXml(CustomMatchUtil.getAttributeResult(data));
                eventService.analyseBPDBCOSEMEvent(notifyData, modelId, deviceType, deviceId, dataItemId);
                return 1;
            } else if ((modelId == DeviceModel.Ameter100_BPDB || modelId == DeviceModel.Ameter300_BPDB) && "DataNotification".equalsIgnoreCase(cosemData.getType())) {
                // 掉电上电暂时进行特殊处理
                List<CosemData> cosemStrData = cosemData.getChildAt(2) == null ? null : cosemData.getChildAt(2).getChildAt(0) == null ?
                        null : cosemData.getChildAt(2).getChildAt(0).getChildAt(0) == null ? null : cosemData.getChildAt(2).getChildAt(0).getChildAt(0).getChild();
                if (CollUtil.isEmpty(cosemStrData) && cosemStrData.size() < 4) {
                    log.info("the pushUp pushDown packet is failed to match the sample packet");
                    return 1;
                }
                CosemData cosemEventCode = cosemStrData.get(3);
                Integer eventCode = Convert.toInt(ByteNumberUtils.byte4ToInt(ByteUtils.hexStringToByte(cosemEventCode.getValue())));
                CosemData cosemDateTime = cosemData.getChildAt(1);
                Date pushDate = (Date) cosemXmlService.getData(cosemDateTime.getType(), cosemDateTime.getValue());

                eventService.processBPDBNotification(modelId, deviceType, deviceId, -1L, pushDate, eventCode, null, null);
            }
        }

        if ("GatewayResponse".equalsIgnoreCase(cosemData.getType()) || "GatewayRequest".equalsIgnoreCase(cosemData.getType())) {
            notifyData = cosemData.getChildAt(2);
        } else if ("EventNotificationRequest".equalsIgnoreCase(cosemData.getType())) {
            CosemData obisData = cosemData.getChildAt(0).getChildAt(1);
            String obis = obisData.getValue();
            dataItemId = assetManagementService.getDateItemIdByObisAndModelId(modelId, obis);
            notifyData = cosemData.getChildAt(1).getChildAt(0);
        } else if ("DataNotification".equalsIgnoreCase(cosemData.getType())) {
            // 下面解析目前为自注册的报文结构
            CosemData obisData = cosemData.getChildAt(2) == null ?
                    null : cosemData.getChildAt(2).getChildAt(0) == null ?
                    null : cosemData.getChildAt(2).getChildAt(0).getChildAt(0) == null ?
                    null : cosemData.getChildAt(2).getChildAt(0).getChildAt(0).getChildAt(0);
            String obis = obisData == null ? null : obisData.getValue();
            if (null != obis) {
                if (OBIS_HEX_PTN.matcher(obis).matches()) {
                    dataItemId = assetManagementService.getDateItemIdByObisAndModelId(modelId, obis);
                }
            }
        }

        eventService.analyseCOSEMEvent(notifyData, modelId, deviceType, deviceId, dataItemId);
        return 1;
    }
}
